﻿package 
{
	import com.bit101.components.Label;
	import com.bit101.components.MinimalComps;
	import flash.display.Sprite;
	import flash.events.Event;
	import flash.events.MouseEvent;
	import flash.text.TextField;
	import graph.*;
	import twitter.*;

	/**
	 * ...
	 * @author s
	 */
    [SWF(width = "640", height = "640", frameRate = "60")]
	public class Main extends Sprite 
	{
		private var _graph:SpringGraph;
		private var _tweetLoader:TweetLoaderFriendTimeline;
		private var _twitterAccessToken:String;
		private var _twitterAccessTokenSecret:String;
		private var _twitterUserScreenName:String;
		private var _twitterUserId:String;
		
		private var _draggingLabel:NodeAttachedLabel;
		private var _focusedLabel:NodeAttachedLabel;
		private var _labels:Vector.<NodeAttachedLabel>;
		private var _wordQueue:Vector.<Array>; // vector of tuple(word, tweet)
		private var _tweet2Labels:Object;
		private var _word2Label:Object;
		
		private const _STOPWORD:Object = {
			'http': true,
			'www': true,
			'RT': true
		}
		
		public function Main():void 
		{
			if (stage) init();
			else addEventListener(Event.ADDED_TO_STAGE, init);
		}
		
		private function init(e:Event = null):void 
		{
			removeEventListener(Event.ADDED_TO_STAGE, init);
			// entry point
			
			_graph = new SpringGraph(this.stage.stageWidth, this.stage.stageHeight);
			_wordQueue = new Vector.<Array>;
			_labels = new Vector.<NodeAttachedLabel>;
			_tweet2Labels = new Object;
			_word2Label = new Object;
			_focusedLabel = null;
			_draggingLabel = null;
			
			var flashVars:Object = this.loaderInfo.parameters;
			_twitterAccessToken = flashVars["twitterAccessToken"];
			_twitterAccessTokenSecret = flashVars["twitterAccessTokenSecret"]
			_twitterUserScreenName = flashVars["twitterUserScreenName"];
			_twitterUserId = flashVars["twitterUserId"];
			
			MinimalComps.setLanguage(MinimalComps.JAPANESE);
			new Label(this, 0, 0, "18");

			_tweetLoader = new TweetLoaderFriendTimeline(_twitterAccessToken, _twitterAccessTokenSecret, TwitterKey.CONSUMER_KEY, TwitterKey.CONSUMER_SECRET);
			_tweetLoader.loadFriendTimeline(loadNewTweet, 50);

			addEventListener(Event.ENTER_FRAME, enterFrameHandler);
			stage.addEventListener(MouseEvent.MOUSE_DOWN, mouseDown);
			stage.addEventListener(MouseEvent.MOUSE_UP, mouseUp);
		}

		private function popFromWordQueueAndCreateLabel():void 
		{
			if (_wordQueue.length > 0 && _labels.length < 100) {
				var wordTuple:Array = _wordQueue.shift();
				var word:String = wordTuple[0];
				var tweet:Tweet = wordTuple[1];
				var label:NodeAttachedLabel;
				
				//trace(word + " : " + tweet.text);
				
				if (_word2Label[word] == undefined) {
					const NUM_CIRCLE_NODES:int = 50;
					var x:Number, y:Number;
					var r:Number, theta:Number;

					if (_labels.length < NUM_CIRCLE_NODES * 1.1) {
						theta = 2 * Math.PI * _labels.length / NUM_CIRCLE_NODES;
						r = (this.stage.stageWidth / 2) * Math.pow(0.9, _labels.length / NUM_CIRCLE_NODES);
					}
					else {
						theta = Math.random() * 2 * Math.PI;
						r = Math.random() * (this.stage.stageWidth / 2) * 0.9;
					}
					x = (this.stage.stageWidth  / 2) + Math.sin(theta) * r * 0.9;
					y = (this.stage.stageHeight / 2) - Math.cos(theta) * r * 0.75;
					label = new NodeAttachedLabel(this, x, y, word, 1);
					label.addEventListener(MouseEvent.MOUSE_OVER, mouseOverToLabel);
					label.addEventListener(MouseEvent.MOUSE_OUT,  mouseOutFromLabel);
					_graph.addNodeToPosition(label.attachedGraphNode, x, y);
					_labels.push(label);
					_word2Label[word] = label;
				}
				else {
					label = _word2Label[word];
					label.attachedGraphNode.weight++;
					label.setWeight(label.attachedGraphNode.weight);
					//trace(word + ": " + label.attachedGraphNode.weight);
				}

				if (_tweet2Labels[tweet.id] == undefined) {
					_tweet2Labels[tweet.id] = new Vector.<NodeAttachedLabel>;
				}
				//trace("word=" + word + "  tweet=" + tweet.text);
				
				// define inner tweet edges
				for each (var adjLabel:NodeAttachedLabel in _tweet2Labels[tweet.id]) {
					if(label != adjLabel){
						_graph.defineNewEdge(label.attachedGraphNode, adjLabel.attachedGraphNode, 1.0);
					}
				}
				
				// define inter tweet edges
				if (_tweet2Labels[tweet.id].length == 0) {
					if (tweet.prevTweet != null) {
						if (_tweet2Labels[tweet.prevTweet.id] != undefined) {
							var adjLabels:Vector.<NodeAttachedLabel> = _tweet2Labels[tweet.prevTweet.id];
							for (var adjLabelCt:int = 0; adjLabelCt < adjLabels.length; adjLabelCt++){
								if (label == adjLabels[adjLabelCt]) {
									continue;
								}
								_graph.defineNewEdge(label.attachedGraphNode, adjLabels[adjLabelCt].attachedGraphNode, 0.5);
								break;
							}
						}
					}
				}

				if(_tweet2Labels[tweet.id].indexOf(label) == -1){
					_tweet2Labels[tweet.id].push(label);
				}
			}
		}

		private function enterFrameHandler(e:Event):void 
		{
			for (var wordCt:int = 0; wordCt < 1 + _labels.length / 16; wordCt++) {
				popFromWordQueueAndCreateLabel();
			}
			
			if (_draggingLabel != null) {
				_graph.setDragNode(_draggingLabel.attachedGraphNode, stage.mouseX, stage.mouseY);
			}
			else {
				_graph.setDragNode(null, 0, 0);
			}

			_graph.step(1.0/60.0, 1);
			
			for (var i:int = 0; i < _labels.length; i++) {
				_labels[i].setPosition(_graph.getNodeX(_labels[i].attachedGraphNode), _graph.getNodeY(_labels[i].attachedGraphNode));
			}
			
			graphics.clear();

			if(_focusedLabel != null || _draggingLabel != null){
				var focusedNode:Node;
				if(_draggingLabel != null){
					focusedNode = _draggingLabel.attachedGraphNode;
				}
				else if(_focusedLabel != null){
					focusedNode = _focusedLabel.attachedGraphNode;
				}
				// draw edges
				graphics.lineStyle(1, 0xA0A0A0);
				for (var edgeCt:int = 0; focusedNode.adjacentEdge(edgeCt) != null; edgeCt++) {
					var edge:Edge = focusedNode.adjacentEdge(edgeCt);
					if (edge.weight < 1.0) {
						continue;
					}
					graphics.moveTo(_graph.getNodeX(edge.srcNode), _graph.getNodeY(edge.srcNode));
					graphics.lineTo(_graph.getNodeX(edge.dstNode), _graph.getNodeY(edge.dstNode));
				}
			}
		}
		
		private function loadNewTweet(tweet:Tweet):void 
		{
			//new Label(this, 0, 20, tweet.text);
			//return;
			var pattern:RegExp = /([一-龠々〆ヵヶ]+)|([ぁ-んー－―]+)|([ァ-ヴー－―]+)|([a-zA-ZａｚＡ-Ｚ]+)|([0-9０-９.]+)|[、。！!？?()（）「」『』-]/g;
			var filterFunc:Function = function (buf:String, index:int, arr:Array):Boolean 
			{
				return !(buf.match(/([ぁ-んー－―]+)|([0-9０-９.]+)|[、。！!？?()（）「」『』-]/) || buf.length == 1);
			};
			var words:Array = tweet.text.match(pattern).filter(filterFunc);
			//trace(words.join("|"));
			
			for each (var word:String in words) {
				if (_STOPWORD[word] == undefined) {
					_wordQueue.push([word, tweet]);
				}
			}
		}
		
		private function mouseOverToLabel(e:Event):void 
		{
			_focusedLabel = e.target as NodeAttachedLabel;
			trace("mouse over to " + _focusedLabel.text);
		}
		
		private function mouseOutFromLabel(e:Event):void 
		{
			trace("mouse out from " + _focusedLabel.text);
			_focusedLabel = null;
		}
		
		private function mouseDown(e:Event):void 
		{
			if (_focusedLabel) {
				trace("dragging:" + _focusedLabel.text + ": " + _focusedLabel.attachedGraphNode.weight);
				_draggingLabel = _focusedLabel;
			}
		}
		
		private function mouseUp(e:Event):void 
		{
			trace("mouse up");
			_draggingLabel = null;
		}
	}
}
